#include <iostream>
#include <string>
#include <vector>
#include "vector.h"

struct Foo
{
    // std::vector<int> v1(8); // 编译失败: 直接初始化不能用在类类型的成员初始化上
    std::vector<int> v{std::vector<int>(8)}; // 创建一个容量为8的std::vector，然后将其用作v的初始值设定项。
};

void vector_init()
{
    // 值初始化（使用默认构造函数）
    std::vector<int> empty{}; // 含有0个 int 得vector

    // 值初始化
    std::vector<int> primes{2, 3, 5, 7}; // vector 包含 4 个 int，分别为 2, 3, 5, 7

    std::vector<int> datas(10); // vector 含有 10 个 int, 值初始化为 0

    // 必须初始化const std::vector，然后不能修改。这样一个vector中的元素被视为常量。
    const std::vector<int> prime{2, 3, 5, 7, 11}; // prime 和其中的元素不允许被修改

    // 获取大小
    std::cout << "size: " << primes.size() << std::endl;
    // 另外一种方式
    std::cout << "size: " << std::size(primes) << std::endl;

    // 获取内容
    // 不进行边界检查
    // 不进行边界检查
    // 因为它对每个调用都进行运行时边界检查，所以at()比运算符[]慢（但更安全）。尽管不太安全，但通常使用操作符[]而不是at()，因为我们会现检查索引在数组长度内，然后再访问，不会首先尝试使用无效的索引
    std::cout << "first element: " << primes[0] << std::endl;
    std::cout << "first element: " << primes[5] << std::endl; // 非法下标 (未定义的行为)

    // 使用at()成员函数访问数组元素执行运行时边界检查
    std::cout << "first element: " << primes.at(0) << std::endl;
    std::cout << "un elemeng" << primes.at(5) << std::endl; // 无效索引 (抛出异常)

    constexpr int index1{3};
    std::cout << prime[index1] << '\n'; // okay: constexpr 索引 隐式转换为 std::size_t, 不是窄化转化

    std::size_t index2{3};              // non-constexpr
    std::cout << prime[index2] << '\n'; // operator[] 参数是 std::size_t, 无需转换

    int index3{3};                      // non-constexpr
    std::cout << prime[index3] << '\n'; // 可能会有 warning: index 隐式转换为 std::size_t, 窄化转换
}

/**
 * 类型为std::vector的对象可以像任何其他对象一样传递给函数。这意味着，如果按值传递std::vector，将生成一个昂贵的副本。因此，通常通过（const）引用传递std::vector以避免这种复制。

对于std::vector，存储的元素类型是对象类型信息的一部分。因此，当使用std::vector作为函数参数时，必须显式指定元素类型
 */
void printByRef(const std::vector<int> &v)
{
    std::cout << "printByRef: " << v[0] << std::endl;
}

// 可以通过定义模板来打印各种类型数据，类似Java的泛型
template <typename T>
void printTpl(const std::vector<T> &v)
{
    std::cout << "printTpl: " << v[0] << std::endl;
}

// c++20 写法
void printAuto(const auto &v)
{
    std::cout << "printAuto: " << v[0] << std::endl;
}

void _copy()
{
    std::vector<int> v1{1, 2, 3};
    std::vector<int> v2{v1}; // 复制数据，将v1复制到v2，这个复制代价是巨大的
    v1[0] = 6;
    v2[0] = 7;
    std::cout << "v1: " << v1[0] << std::endl;
    std::cout << "v2: " << v2[0] << std::endl;
}

std::vector<int> generate()
{
    std::vector<int> v{1, 2, 3};
    return v;
}

void right_way()
{
    // 这里使用到了右值表达式，在表达式结束前函数返回的数据都是有效的，当表达式结束时就会销毁数据
    // 然后这里又使用了数组数据复制这样就将临时数据保存到到了新的数组种，但是这个代价是巨大的
    std::vector<int> v1{generate()};
    std::cout << "v1: " << v1[0] << std::endl;
}
